/*
 * UAE - The Un*x Amiga Emulator
 *
 * Main program
 *
 * Copyright 1995 Ed Hanway
 * Copyright 1995, 1996, 1997 Bernd Schmidt
 */

#include "sysconfig.h"
#include "gensound.h"
#include "serial.h"
#include "dxwrap.h"
#include "picasso96_win.h"
#include "uaeexe.h"
#include "uaeserial.h"
#include "parallel.h"
#include "ncr_scsi.h"
#include "scsi.h"
#include "sana2.h"
#include "uaeresource.h"
#include "dongle.h"
#include "sampler.h"
#include "consolehook.h"
#include "options.h"
#include "akiko.h"
#include "audio.h"
#include "sound.h"
#include "newcpu.h"
#include "native2amiga.h"
#include "blkdev.h"
#include "cdtv.h"
#include "rommgr.h"
#include "keyboard.h"
#include "inputdevice.h"
#include "savestate.h"
#include "debug.h"
#include "uae.h"
#include "gui.h"
#include "drawing.h"
#include "bsdsocket.h"
#include "zfile.h"
#include "disk.h"
#include "scsidev.h"
#include "a2091.h"
#include "win32.h"
#include "a2065.h"
#include "keybuf.h"
#include "filesys.h"
#include "hardfile.h"
#include "gayle.h"
#include "readcpu.h"

long int version = 256 * 65536L * UAEMAJOR + 65536L * UAEMINOR + UAESUBREV;

int config_changed;

bool no_gui = false /*, quit_to_gui = 0*/;
// bool cloanto_rom = 0;
bool kickstart_rom = 1;
bool console_emulation = 0;

struct gui_info gui_data;

TCHAR warning_buffer[256];

TCHAR optionsfile[256];

static uint randseed;
static int oldhcounter;

uint uaesrand(uint seed)
{
    oldhcounter = -1;
    randseed = seed;
    // randseed = 0x12345678;
    // Logger::Write(L"seed=%08x\n", randseed);
    return randseed;
}
uint uaerand()
{
    if (oldhcounter != hsync_counter)
    {
        srand(hsync_counter ^ randseed);
        oldhcounter = hsync_counter;
    }
    uint r = rand();
    // Logger::Write(L"rand=%08x\n", r);
    return r;
}
uint uaerandgetseed()
{
    return randseed;
}

// void discard_prefs(CConfiguration* p, int type)
// {
//     struct strlist** ps = &p->all_lines;
//     while (*ps)
//     {
//         struct strlist* s = *ps;
//         *ps = s->next;
//         free(s->value);
//         free(s->option);
//         free(s);
//     }
//     #ifdef FILESYS
//     filesys_cleanup();
//     #endif
// }

static void fixup_prefs_dim2(struct wh* wh)
{
    if (wh->width < 160)
        wh->width = 160;
    if (wh->height < 128)
        wh->height = 128;
    if (wh->width > 3072)
        wh->width = 3072;
    if (wh->height > 2048)
        wh->height = 2048;
}

void fixup_prefs_dimensions(CConfiguration* prefs)
{
    fixup_prefs_dim2(&prefs->gfx_size_fs);
    fixup_prefs_dim2(&prefs->gfx_size_win);
    if (prefs->gfx_filter_autoscale == AUTOSCALE_RESIZE)
        prefs->gfx_filter_zoom_mult = 0;
}

void fixup_cpu(CConfiguration* p)
{
    // if (p->cpu_frequency == 1000000)
    //     p->cpu_frequency = 0;

    switch (p->cpu_model)
    {
        case 68000:
        // p->address_space_24 = 1;
        // if (p->cpu_compatible || p->cpu_cycle_exact)
        //     p->fpu_model = 0;
        // break;
        case 68010:

            ASSERT(p->address_space_24 == 1);
            p->address_space_24 = 1;

            if (p->cpu_compatible || p->cpu_cycle_exact)
            {
                ASSERT(p->fpu_model == 0);
                p->fpu_model = 0;
            }
            break;
        case 68020:
            break;
        case 68030:

            ASSERT(p->address_space_24 == 0);
            p->address_space_24 = 0;
            break;

        case 68040:

            ASSERT(p->address_space_24 == 0);
            p->address_space_24 = 0;
            if (p->fpu_model)
            {
                ASSERT(p->fpu_model == 68040);
                p->fpu_model = 68040;
            }
            break;

        case 68060:

            ASSERT(p->address_space_24 == 0);
            p->address_space_24 = 0;
            if (p->fpu_model)
            {
                ASSERT(p->fpu_model == 68060);
                p->fpu_model = 68060;
            }
            break;
    }

    if (p->cpu_model != 68040)
        p->mmu_model = 0;

    if (p->cpu_cycle_exact)
        if (p->fpu_model != 68040)
            if (p->fpu_model != 68060)
                p->fpu_model = 0;
    if ((p->m68k_speed == 0) && !p->cpu_cycle_exact)
        p->cpu_idle = 0;
}

void fixup_prefs(CConfiguration* p)
{
    // int err = 0;

    built_in_chipset_prefs(p);
    fixup_cpu(p);

    // if (((p->chipmem_size & (p->chipmem_size - 1)) != 0 && p->chipmem_size != 0x180000)
    //     || p->chipmem_size < 0x20000
    //     || p->chipmem_size > 0x800000)
    // {
    //     Logger::Write(L"Unsupported chipmem size %x!\n", p->chipmem_size);
    //     p->chipmem_size = 0x200000;
    //     err = 1;
    // }
    // if ((p->fastmem_size & (p->fastmem_size - 1)) != 0
    //     || (p->fastmem_size != 0 && (p->fastmem_size < 0x100000 || p->fastmem_size > 0x800000)))
    // {
    //     Logger::Write(L"Unsupported fastmem size %x!\n", p->fastmem_size);
    //     err = 1;
    // }
    // if ((p->gfxmem_size & (p->gfxmem_size - 1)) != 0
    //     || (p->gfxmem_size != 0 && (p->gfxmem_size < 0x100000 || p->gfxmem_size > max_z3fastmem / 2)))
    // {
    //     Logger::Write(L"Unsupported graphics card memory size %x (%x)!\n", p->gfxmem_size, max_z3fastmem / 2);
    //     if (p->gfxmem_size > max_z3fastmem / 2)
    //         p->gfxmem_size = max_z3fastmem / 2;
    //     else
    //         p->gfxmem_size = 0;
    //     err = 1;
    // }
    // if ((p->z3fastmem_size & (p->z3fastmem_size - 1)) != 0
    //     || (p->z3fastmem_size != 0 && (p->z3fastmem_size < 0x100000 || p->z3fastmem_size > max_z3fastmem)))
    // {
    //     Logger::Write(L"Unsupported Zorro III fastmem size %x (%x)!\n", p->z3fastmem_size, max_z3fastmem);
    //     if (p->z3fastmem_size > max_z3fastmem)
    //         p->z3fastmem_size = max_z3fastmem;
    //     else
    //         p->z3fastmem_size = 0;
    //     err = 1;
    // }
    // if ((p->z3fastmem2_size & (p->z3fastmem2_size - 1)) != 0
    //     || (p->z3fastmem2_size != 0 && (p->z3fastmem2_size < 0x100000 || p->z3fastmem2_size > max_z3fastmem)))
    // {
    //     Logger::Write(L"Unsupported Zorro III fastmem size %x (%x)!\n", p->z3fastmem2_size, max_z3fastmem);
    //     if (p->z3fastmem2_size > max_z3fastmem)
    //         p->z3fastmem2_size = max_z3fastmem;
    //     else
    //         p->z3fastmem2_size = 0;
    //     err = 1;
    // }
    // p->z3fastmem_start &= ~0xffff;
    // if (p->z3fastmem_start < 0x1000000)
    //     p->z3fastmem_start = 0x1000000;
    // if ((p->z3chipmem_size & (p->z3chipmem_size - 1)) != 0
    //     || (p->z3chipmem_size != 0 && (p->z3chipmem_size < 0x100000 || p->z3chipmem_size > max_z3fastmem)))
    // {
    //     Logger::Write(L"Unsupported Zorro III fake chipmem size %x (%x)!\n", p->z3chipmem_size, max_z3fastmem);
    //     if (p->z3chipmem_size > max_z3fastmem)
    //         p->z3chipmem_size = max_z3fastmem;
    //     else
    //         p->z3chipmem_size = 0;
    //     err = 1;
    // }
    //
    // if (p->address_space_24 && (p->gfxmem_size != 0 || p->z3fastmem_size != 0))
    // {
    //     p->z3fastmem_size = p->gfxmem_size = 0;
    //     Logger::Write(L"Can't use a graphics card or Zorro III fastmem when using a 24 bit\n"
    //               L"address space - sorry.\n");
    // }
    // if (p->bogomem_size != 0 && p->bogomem_size != 0x80000 && p->bogomem_size != 0x100000 && p->bogomem_size != 0x180000 && p->bogomem_size != 0x1c0000)
    // {
    //     p->bogomem_size = 0;
    //     Logger::Write(L"Unsupported bogomem size!\n");
    //     err = 1;
    // }
    // if (p->slowmem_size > 0x180000 && (p->cs_fatgaryrev >= 0 || p->cs_ide || p->cs_ramseyrev >= 0))
    // {
    //     p->slowmem_size = 0x180000;
    //     Logger::Write(L"Possible Gayle bogomem conflict fixed\n");
    // }
    // if (p->chipmem_size > 0x200000 && p->fastmem_size != 0)
    // {
    //     Logger::Write(L"You can't use fastmem and more than 2MB chip at the same time!\n");
    //     p->fastmem_size = 0;
    //     err = 1;
    // }
    // if (p->mbresmem_low_size > 0x04000000 || (p->mbresmem_low_size & 0xfffff))
    // {
    //     p->mbresmem_low_size = 0;
    //     Logger::Write(L"Unsupported A3000 MB RAM size\n");
    // }
    // if (p->mbresmem_high_size > 0x04000000 || (p->mbresmem_high_size & 0xfffff))
    // {
    //     p->mbresmem_high_size = 0;
    //     Logger::Write(L"Unsupported Motherboard RAM size\n");
    // }
    //
    // #if 0
    // if (p->m68k_speed < -1 || p->m68k_speed > 20)
    // {
    //     Logger::Write(L"Bad value for -w parameter: must be -1, 0, or within 1..20.\n");
    //     p->m68k_speed = 4;
    //     err = 1;
    // }
    // #endif
    //
    // /*if (p->produce_sound < 0 || p->produce_sound > 3)
    // {
    //     Logger::Write(L"Bad value for -S parameter: enable value must be within 0..3\n");
    //     p->produce_sound = 0;
    //     err = 1;
    // }*/
    // if (p->comptrustbyte < 0 || p->comptrustbyte > 3)
    // {
    //     Logger::Write(L"Bad value for comptrustbyte parameter: value must be within 0..2\n");
    //     p->comptrustbyte = 1;
    //     err = 1;
    // }
    // if (p->comptrustword < 0 || p->comptrustword > 3)
    // {
    //     Logger::Write(L"Bad value for comptrustword parameter: value must be within 0..2\n");
    //     p->comptrustword = 1;
    //     err = 1;
    // }
    // if (p->comptrustlong < 0 || p->comptrustlong > 3)
    // {
    //     Logger::Write(L"Bad value for comptrustlong parameter: value must be within 0..2\n");
    //     p->comptrustlong = 1;
    //     err = 1;
    // }
    // if (p->comptrustnaddr < 0 || p->comptrustnaddr > 3)
    // {
    //     Logger::Write(L"Bad value for comptrustnaddr parameter: value must be within 0..2\n");
    //     p->comptrustnaddr = 1;
    //     err = 1;
    // }
    // if (p->jit_cache_size < 0 || p->jit_cache_size > 16384)
    // {
    //     Logger::Write(L"Bad value for cachesize parameter: value must be within 0..16384\n");
    //     p->jit_cache_size = 0;
    //     err = 1;
    // }
    // if (p->z3fastmem_size > 0 && (p->address_space_24 || p->cpu_model < 68020))
    // {
    //     Logger::Write(L"Z3 fast memory can't be used with a 68000/68010 emulation. It\n"
    //               L"requires a 68020 emulation. Turning off Z3 fast memory.\n");
    //     p->z3fastmem_size = 0;
    //     err = 1;
    // }
    // if (p->gfxmem_size > 0 && (p->cpu_model < 68020 || p->address_space_24))
    // {
    //     Logger::Write(L"Picasso96 can't be used with a 68000/68010 or 68EC020 emulation. It\n"
    //               L"requires a 68020 emulation. Turning off Picasso96.\n");
    //     p->gfxmem_size = 0;
    //     err = 1;
    // }
    // #if !defined (BSDSOCKET)
    // if (p->socket_emu)
    // {
    //     Logger::Write(L"Compile-time option of BSDSOCKET_SUPPORTED was not enabled.  You can't use bsd-socket emulation.\n");
    //     p->socket_emu = 0;
    //     err = 1;
    // }
    // #endif
    //
    // if (p->nr_floppies < 0 || p->nr_floppies > 4)
    // {
    //     Logger::Write(L"Invalid number of floppies.  Using 4.\n");
    //     p->nr_floppies = 4;
    //     p->floppies[0].dfxtype = 0;
    //     p->floppies[1].dfxtype = 0;
    //     p->floppies[2].dfxtype = 0;
    //     p->floppies[3].dfxtype = 0;
    //     err = 1;
    // }
    // if (p->floppy_speed > 0 && p->floppy_speed < 10)
    // {
    //     p->floppy_speed = 100;
    // }
    // if (p->input_mouse_speed < 1 || p->input_mouse_speed > 1000)
    // {
    //     p->input_mouse_speed = 100;
    // }
    // if (p->collision_level < 0 || p->collision_level > 3)
    // {
    //     Logger::Write(L"Invalid collision support level.  Using 1.\n");
    //     p->collision_level = 1;
    //     err = 1;
    // }
    // if (p->parallel_postscript_emulation)
    //     p->parallel_postscript_detection = 1;
    // if (p->cs_compatible == 1)
    // {
    //     p->cs_fatgaryrev = p->cs_ramseyrev = p->cs_mbdmac = -1;
    //     p->cs_ide = 0;
    //     if (p->cpu_model >= 68020)
    //     {
    //         p->cs_fatgaryrev = 0;
    //         p->cs_ide = -1;
    //         p->cs_ramseyrev = 0x0f;
    //         p->cs_mbdmac = 0;
    //     }
    // }
    // else if (p->cs_compatible == 0)
    // {
    //     if (p->cs_ide == IDE_A4000)
    //     {
    //         if (p->cs_fatgaryrev < 0)
    //             p->cs_fatgaryrev = 0;
    //         if (p->cs_ramseyrev < 0)
    //             p->cs_ramseyrev = 0x0f;
    //     }
    // }
    fixup_prefs_dimensions(p);

    // #if !defined (JIT)
    // p->jit_cache_size = 0;
    // #endif
    // #ifdef CPU_68000_ONLY
    // p->cpu_model = 68000;
    // p->fpu_model = 0;
    // #endif
    #ifndef CPUEMU_0
    p->cpu_compatible = 1;
    p->address_space_24 = 1;
    #endif

    #if !defined (CPUEMU_11) && !defined (CPUEMU_12)
    p->cpu_compatible = 0;
    p->address_space_24 = 0;
    #endif
    #if !defined (CPUEMU_12)
    p->cpu_cycle_exact = p->blitter_cycle_exact = 0;
    #endif

    #ifndef AGA
    p->chipset_mask &= ~CSMASK_AGA;
    #endif

    #ifndef AUTOCONFIG
    p->z3fastmem_size = 0;
    p->fastmem_size = 0;
    p->gfx_mem_size = 0;
    #endif
    #if !defined (BSDSOCKET)
    p->socket_emu = 0;
    #endif
    #if !defined (SCSIEMU)
    p->scsi = 0;
    p->win32_aspi = 0;
    #endif
    #if !defined (SANA2)
    p->sana2 = 0;
    #endif
    #if !defined (UAESERIAL)
    p->uaeserial = 0;
    #endif

    #if defined (CPUEMU_12)
    if (p->cpu_cycle_exact)
    {
        p->gfx_framerate = 1;
        p->jit_cache_size = 0;
        p->m68k_speed = 0;
    }
    #endif
    // if (p->maprom && !p->address_space_24)
    //     p->maprom = 0x0f000000;
    if (p->tod_hack && p->cs_ciaatod == 0)
        p->cs_ciaatod = 3;

    blkdev_fix_prefs(p);
    // target_fixup_options(p);
}

int quit_program = 0;
/*static*/ int restart_program;
static TCHAR restart_config[MAX_DPATH];
static int default_config;

void uae_reset(int hardreset)
{
    if (debug_dma)
    {
        record_dma_reset();
        record_dma_reset();
    }

    // g_curr_conf.quitstatefile[0] = g_changed_conf.quitstatefile[0] = 0;

    if (quit_program == 0)
    {
        quit_program = -2;
        if (hardreset)
            quit_program = -3;
    }
}

void uae_quit()
{
    deactivate_debugger();
    if (quit_program != -1)
        quit_program = -1;
    // target_quit();
}

/* 0 = normal, 1 = nogui, -1 = disable nogui */
void uae_restart(UaeRestart mode, TCHAR* cfgfile)
{
    uae_quit();
    restart_program = (mode == UaeRestart_NoGUI) ? RESTART_PROGRAM_INIT :
        ((mode == UaeRestart_Normal) ? RESTART_PROGRAM_RESET : RESTART_PROGRAM_RESTART);
    restart_config[0] = 0;
    default_config = 0;
    if (cfgfile)
        _tcscpy(restart_config, cfgfile);
}

// #ifndef DONT_PARSE_CMDLINE

// void usage()
// {
// }
// static void parse_cmdline_2(int argc, TCHAR** argv)
// {
//     int i;
//
//     cfgfile_addcfgparam(0);
//     for (i = 1; i < argc; i++)
//     {
//         if (_tcsncmp(argv[i], L"-cfgparam=", 10) == 0)
//         {
//             cfgfile_addcfgparam(argv[i] + 10);
//         }
//         else if (_tcscmp(argv[i], L"-cfgparam") == 0)
//         {
//             if (i + 1 == argc)
//                 Logger::Write(L"Missing argument for '-cfgparam' option.\n");
//             else
//                 cfgfile_addcfgparam(argv[++i]);
//         }
//     }
// }
//
// static void parse_diskswapper(TCHAR* s)
// {
//     TCHAR* tmp = _tcsdup(s);
//     TCHAR* delim = L",";
//     TCHAR* p1, * p2;
//     int num = 0;
//
//     p1 = tmp;
//     for (;;)
//     {
//         p2 = _tcstok(p1, delim);
//         if (!p2)
//             break;
//         p1 = nullptr;
//         if (num >= MAX_SPARE_DRIVES)
//             break;
//         _tcsncpy(g_curr_conf.dfxlist[num], p2, 255);
//         num++;
//     }
//     free(tmp);
// }

static TCHAR* parsetext(const TCHAR* s)
{
    if (*s == '"' || *s == '\'')
    {
        TCHAR* d;
        TCHAR c = *s++;
        int i;
        d = _tcsdup(s);
        for (i = 0; i < _tcslen(d); i++)
        {
            if (d[i] == c)
            {
                d[i] = 0;
                break;
            }
        }
        return d;
    }
    else
    {
        return _tcsdup(s);
    }
}
static TCHAR* parsetextpath(const TCHAR* s)
{
    TCHAR* s2 = parsetext(s);
    TCHAR* s3 = target_expand_environment(s2);
    free(s2);
    return s3;
}

/*static*/ void parse_cmdline(int argc, TCHAR** argv)
{
    int i;

    for (i = 1; i < argc; i++)
    {
        // if (!_tcsncmp(argv[i], L"-diskswapper=", 13))
        // {
        //     TCHAR* txt = parsetextpath(argv[i] + 13);
        //     parse_diskswapper(txt);
        //     free(txt);
        // }
        // else if (_tcsncmp(argv[i], L"-cfgparam=", 10) == 0)
        // {
        //     ;
        // }
        // else if (_tcscmp(argv[i], L"-cfgparam") == 0)
        // {
        //     if (i + 1 < argc)
        //         i++;
        // }
        // else if (_tcsncmp(argv[i], L"-config=", 8) == 0)
        // {
        //     TCHAR* txt = parsetextpath(argv[i] + 8);
        //     g_curr_conf.mountitems = 0;
        //     target_cfgfile_load(&g_curr_conf, txt, -1, 0);
        //     free(txt);
        // }
        // else if (_tcsncmp(argv[i], L"-statefile=", 11) == 0)
        // {
        //     TCHAR* txt = parsetextpath(argv[i] + 11);
        //     savestate_state = STATE_DORESTORE;
        //     _tcscpy(savestate_fname, txt);
        //     free(txt);
        // }
        // else if (_tcscmp(argv[i], L"-f") == 0)
        // {
        //     /* Check for new-style "-f xxx" argument, where xxx is config-file */
        //     if (i + 1 == argc)
        //     {
        //         Logger::Write(L"Missing argument for '-f' option.\n");
        //     }
        //     else
        //     {
        //         TCHAR* txt = parsetextpath(argv[++i]);
        //         g_curr_conf.mountitems = 0;
        //         target_cfgfile_load(&g_curr_conf, txt, -1, 0);
        //         free(txt);
        //     }
        // }
        // else if (_tcscmp(argv[i], L"-s") == 0)
        // {
        //     if (i + 1 == argc)
        //         Logger::Write(L"Missing argument for '-s' option.\n");
        //     else
        //         cfgfile_parse_line(&g_curr_conf, argv[++i], 0);
        // }
        // else if (_tcscmp(argv[i], L"-h") == 0 || _tcscmp(argv[i], L"-help") == 0)
        // {
        //     usage();
        //     exit(0);
        // }
        // else if (_tcsncmp(argv[i], L"-cdimage=", 9) == 0)
        // {
        //     TCHAR* txt = parsetextpath(argv[i] + 9);
        //     TCHAR* txt2 = xmalloc(TCHAR, _tcslen(txt) + 2);
        //     _tcscpy(txt2, txt);
        //     if (_tcsrchr(txt2, ',') != nullptr)
        //         _tcscat(txt2, L",");
        //     cfgfile_parse_option(&g_curr_conf, L"cdimage0", txt2, 0);
        //     free(txt2);
        //     free(txt);
        // }
        // else
        // {
        if (argv[i][0] == '-' && argv[i][1] != '\0')
            // {
            //     const TCHAR* arg = argv[i] + 2;
            //     int extra_arg = *arg == '\0';
            //     if (extra_arg)
            //         arg = i + 1 < argc ? argv[i + 1] : 0;
            parse_cmdline_option(&g_curr_conf, argv[i][1]);
        //         i++;
        // }
        // }
    }
}
// #endif

static void parse_cmdline_and_init_file(int argc, TCHAR** argv)
{
    _tcscpy(optionsfile, L"");

    // #ifdef OPTIONS_IN_HOME
    // {
    //     TCHAR* home = getenv("HOME");
    //     if (home != nullptr && strlen(home) < 240)
    //     {
    //         _tcscpy(optionsfile, home);
    //         _tcscat(optionsfile, L"/");
    //     }
    // }
    // #endif

    // parse_cmdline_2(argc, argv);

    _tcscat(optionsfile, restart_config);

    if (!target_cfgfile_load(&g_curr_conf, optionsfile, 0, default_config))
    {
        if (Logging::CONFIG_DEBUG)
            Logger::Write(L"failed to load config '%s'\n", optionsfile);
        // #ifdef OPTIONS_IN_HOME
        // /* sam: if not found in $HOME then look in current directory */
        // _tcscpy(optionsfile, restart_config);
        // target_cfgfile_load(&g_curr_conf, optionsfile, 0);
        // #endif
    }
    fixup_prefs(&g_curr_conf);

    parse_cmdline(argc, argv);
}

void reset_all_systems()
{
    init_eventtab();

    #ifdef PICASSO96
    picasso_reset();
    #endif
    #ifdef SCSIEMU
    scsi_reset();
    scsidev_reset();
    scsidev_start_threads();
    #endif
    #ifdef A2065
    a2065_reset();
    #endif
    #ifdef SANA2
    netdev_reset();
    netdev_start_threads();
    #endif
    #ifdef FILESYS
    filesys_prepare_reset();
    filesys_reset();
    #endif
    memory_reset();
    #if defined (BSDSOCKET)
    bsdlib_reset();
    bdslib_free();
    #endif
    #ifdef FILESYS
    filesys_start_threads();
    hardfile_reset();
    #endif
    #ifdef UAESERIAL
    uaeserialdev_reset();
    uaeserialdev_start_threads();
    #endif
    // #if defined (PARALLEL_PORT)
    // initparallel();
    // #endif
    native2amiga_reset();
    dongle_reset();
    sampler_init();
}

/* Okay, this stuff looks strange, but it is here to encourage people who
 * port UAE to re-use as much of this code as possible. Functions that you
 * should be using are do_start_program () and do_leave_program (), as well
 * as real_main (). Some OSes don't call main () (which is braindamaged IMHO,
 * but unfortunately very common), so you need to call real_main () from
 * whatever entry point you have. You may want to write your own versions
 * of start_program () and leave_program () if you need to do anything special.
 * Add #ifdefs around these as appropriate.
 */

int DummyException(int n_except, LPEXCEPTION_POINTERS blah)
{
    ASSERT(FALSE);
    Logger::Write(L"### EXCEPTION %s", __FUNCTION__);

    return EXCEPTION_CONTINUE_SEARCH;
}

void start_program()
{
    if (quit_program == -1)
        return;
    // if (!canbang && candirect < 0)
    //     candirect = 0;
    // if (canbang && candirect < 0)
    //     candirect = 1;
    /* Do a reset on startup. Whether this is elegant is debatable. */
    inputdevice_updateconfig(&g_curr_conf);
    if (quit_program >= 0)
        quit_program = 2;

    __try
    {
        m68k_go(1);
    }
    __except(DummyException(GetExceptionCode(), GetExceptionInformation()))
    {
    }
}

void leave_program()
{
    sampler_free();
    graphics_leave();
    // inputdevice_close();
    DISK_free();
    close_sound();
    dump_counts();
    #ifdef SERIAL_PORT
    serial_exit();
    #endif
    #ifdef CDTV
    cdtv_free();
    #endif
    #ifdef A2091
    a2091_free();
    #endif
    #ifdef NCR
    ncr_free();
    #endif
    #ifdef CD32
    akiko_free();
    #endif
    // if (!no_gui)
    gui_exit();
    // #ifdef USE_SDL
    // SDL_Quit();
    // #endif
    #ifdef AUTOCONFIG
    expansion_cleanup();
    #endif
    #ifdef FILESYS
    filesys_cleanup();
    #endif
    #ifdef BSDSOCKET
    bsdlib_reset();
    #endif
    device_func_reset();
    savestate_free();
    memory_cleanup();
    // cfgfile_addcfgparam(0);
    // machdep_free();

    hardfile_free();
    native2amiga_free();

    picasso_free();

    // romlist_clear();
    readcpu_free();
    gayle_free();
    drawing_free();
}

// void leave_program()
// {
//     do_leave_program();
// }

static int real_main2(int argc, TCHAR** argv)
{
    // #ifdef USE_SDL
    // SDL_Init(SDL_INIT_TIMER | SDL_INIT_AUDIO | SDL_INIT_JOYSTICK | SDL_INIT_NOPARACHUTE);
    // #endif
    config_changed = 1;
    if (restart_config[0])
    {
        default_prefs(&g_curr_conf);
        fixup_prefs(&g_curr_conf);
    }

    if (!graphics_setup())
    {
        exit(1);
    }

    // #ifdef NATMEM_OFFSET
    // preinit_shm();
    // #endif

    if (restart_config[0])
        parse_cmdline_and_init_file(argc, argv);
    else
        g_curr_conf = g_changed_conf;

    if (!machdep_init())
    {
        restart_program = RESTART_PROGRAM_QUIT;
        return -1;
    }

    if (console_emulation)
    {
        consolehook_config(&g_curr_conf);
        fixup_prefs(&g_curr_conf);
    }

    if (!setup_sound())
    {
        Logger::Write(L"Sound driver unavailable: Sound output disabled\n");
        g_curr_conf.sound_produce = false;
    }
    inputdevice_init();

    g_changed_conf = g_curr_conf;
    no_gui = false;
    if (restart_program == RESTART_PROGRAM_RESET)
        no_gui = true;
    else if (restart_program == RESTART_PROGRAM_RESTART)
        no_gui = false;
    restart_program = RESTART_PROGRAM_QUIT;
    if (!no_gui)
    {
        int err = gui_init();
        g_curr_conf = g_changed_conf;
        config_changed = 1;
        if (err == -1)
        {
            Logger::Write(L"Failed to initialize the GUI\n");
            return -1;
        }
        else if (err == -2)
        {
            return 1;
        }
    }

    logging_init();      /* Yes, we call this twice - the first case handles when the user has loaded
                            a config using the cmd-line.  This case handles loads through the GUI. */

    // #ifdef NATMEM_OFFSET
    // init_shm();
    // #endif

    // #ifdef JIT
    // canjit = (g_curr_conf.cpu_model >= 68020) && !g_curr_conf.address_space_24 && (g_curr_conf.jit_cache_size > 0);
    // #endif

    fixup_prefs(&g_curr_conf);
    g_changed_conf = g_curr_conf;
    // target_run();
    /* force sound settings change */
    g_curr_conf.sound_produce = false;

    #ifdef AUTOCONFIG
    rtarea_setup();
    #endif
    #ifdef FILESYS
    rtarea_init();
    uaeres_install();
    hardfile_install();
    #endif
    savestate_init();
    #ifdef SCSIEMU
    scsi_reset();
    scsidev_install();
    #endif
    #ifdef SANA2
    netdev_install();
    #endif
    #ifdef UAESERIAL
    uaeserialdev_install();
    #endif
    keybuf_init();      /* Must come after init_joystick */

    #ifdef AUTOCONFIG
    expansion_init();
    #endif
    #ifdef FILESYS
    filesys_install();
    #endif
    memory_init();
    memory_reset();

    #ifdef AUTOCONFIG
    #if defined (BSDSOCKET)
    bsdlib_install();
    #endif
    emulib_install();
    uaeexe_install();
    native2amiga_install();
    #endif

    custom_init();      /* Must come after memory_init */
    #ifdef SERIAL_PORT
    serial_init();
    #endif
    DISK_init();

    reset_frame_rate_hack();
    init_m68k();      /* must come after reset_frame_rate_hack(); */

    gui_update();

    if (graphics_init())
    {
        // setup_brkhandler();
        if (Debugger::start_debugger)
            activate_debugger();

        if (!init_audio())
        {
            if (sound_available && g_curr_conf.sound_produce)
            {
                Logger::Write(L"Sound driver unavailable: Sound output disabled\n");
            }
            g_curr_conf.sound_produce = false;
        }

        start_program();
    }
    return 0;
}

void real_main(int argc, TCHAR** argv)
{
    restart_program = RESTART_PROGRAM_INIT;

    fetch_configurationpath(restart_config, sizeof(restart_config) / sizeof(TCHAR));
    _tcscat(restart_config, L"default.uae");
    default_config = 1;

    while (restart_program)
    {
        // int ret;
        g_changed_conf = g_curr_conf;

        /*ret =*/ real_main2(argc, argv);
        // if (ret == 0 && quit_to_gui)
        //     restart_program = 1;
        leave_program();
        quit_program = 0;
    }

    romlist_clear();
    inputdevice_close();
    zfile_exit();
}

#ifdef SINGLEFILE
byte singlefile_config[50000] = { "_CONFIG_STARTS_HERE" };
byte singlefile_data[1500000] = { "_DATA_STARTS_HERE" };
#endif